/*
 * ant_clustering_problem.h
 *
 *  Created on: Oct 26, 2011
 *      Author: andrey e clayson
 */

#ifndef ANT_CLUSTERING_PROBLEM_H_
#define ANT_CLUSTERING_PROBLEM_H_

#include <cstdlib>
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <cstring>
#include <vector>
#include "CImg.h"
#include "AntClustering.h"
#include "Data.h"


using namespace std;
using namespace cimg_library;

void pera(string s){
	cout << "\n" << s << "\n";
	string x;
	cin >> x;
}

typedef struct{
	int x,y;
}Dimension;

CImg<int> update_display(Data *nDspace,int num_amostras,int **grid,int **workspace,Point dim_grid,CImg<int> cimg_grid_workspace);
void destroy_matrix(int **matrix,Point dim_matrix);
int **build_matrix(Point dim_matrix);
int **copy_matrix(int **matrix,Point dim_matrix);
double **load_fileToArray(const char* fileName, Dimension *dim_data);
int count_lines(const char* fileName);
void printData(double **data, int x, int y);
void print_grid_workspace(int **grid_workspace,Point dim);
void split(const std::string & str, const string & delimiters, std::vector<std::string> * elems);
Data *load_data(double **data, Dimension dim_data);
void transform_to_grid(Point dim_grid,Data *nDspace,int num_data,int neighborhood_size);
bool has_data(Data *nDspace, int num_data, Point point);
double cosine_similarity(double *v1,double *v2,int length);
void initialize_grid_workspace(int **grid,int **workspace,Point dim_grid,Data *nDspace,int num_data,AntClustering *formigas,int num_formigas);
double get_euclidian_distance(double *v1,double *v2,int length);
double get_grid_euclidian_distance(Point p1,Point p2);
double get_max_euclidian_distance(Data *nDspace,int num_amostras);
double d_intra(Data *nDspace,int num_amostras,int cluster_id);
double d_inter(Data *nDspace,int num_amostras);
int count_num_clusters(Data *nDspace,int num_amostras);


//------------------------------------------------------------------------------
double d_intra(Data *nDspace,int num_amostras,int cluster_id){

	double avg_d_intra = 0.00000;
	int count = 0;

	for(int i=0;i<num_amostras;i++){
		for(int j=0;j<num_amostras;j++){
			if(nDspace[i].get_cluster() == cluster_id && nDspace[j].get_cluster() == cluster_id){
				avg_d_intra += get_grid_euclidian_distance(nDspace[i].get_grid_position(),nDspace[j].get_grid_position());
				count++;
			}
		}
	}

	return avg_d_intra/(double)count;
}
//------------------------------------------------------------------------------
double d_inter(Data *nDspace,int num_amostras){
	double avg_d_inter = 0.00000;
	int count = 0;

	for(int i=0;i<num_amostras;i++){
		for(int j=0;j<num_amostras;j++){
			if(nDspace[i].get_cluster() != nDspace[j].get_cluster()){
				avg_d_inter += get_grid_euclidian_distance(nDspace[i].get_grid_position(),nDspace[j].get_grid_position());
				count++;
			}
		}
	}

	return avg_d_inter/(double)count;
}
//------------------------------------------------------------------------------
int count_num_clusters(Data *nDspace,int num_amostras){
	int max = 0;
	for(int i=0;i<num_amostras;i++){
		if(nDspace[i].get_cluster() > max)
			max = nDspace[i].get_cluster();
	}

	return max+1;
}
//------------------------------------------------------------------------------
CImg<int> update_display(Data *nDspace,int num_amostras,int **grid,int **workspace,Point dim_grid,CImg<int> cimg_grid_workspace){

	//cores dos clusters
	//maximo de 3 clusters
	// cluster 0 - cor red
	// cluster 1 - cor green
	// cluster 2 - cor blue

	//cores das formigas
	// formiga procurando - flag 8 - cor preta
	// formiga carregando - flag 4 - cor amarelo

	//deixando tudo branco
	cimg_forXY(cimg_grid_workspace,x,y){
		cimg_grid_workspace(x,y,0) = cimg_grid_workspace(x,y,1) = cimg_grid_workspace(x,y,2) = 255;
	}


	//atualizando com dados da grid
	int i = 0, j = 0;
	for(int k=0;k<num_amostras;k++){
		if (nDspace[k].get_status()) {
			i = nDspace[k].get_grid_position().x;
			j = nDspace[k].get_grid_position().y;
			if (nDspace[k].get_cluster() == 0) { //cor red
				cimg_grid_workspace(i, j, 0) = 255;
				cimg_grid_workspace(i, j, 1) = 0;
				cimg_grid_workspace(i, j, 2) = 0;
			} else if (nDspace[k].get_cluster() == 1) { //cor green
				cimg_grid_workspace(i, j, 0) = 0;
				cimg_grid_workspace(i, j, 1) = 255;
				cimg_grid_workspace(i, j, 2) = 0;
			} else if (nDspace[k].get_cluster() == 2) { //cor blue
				cimg_grid_workspace(i, j, 0) = 0;
				cimg_grid_workspace(i, j, 1) = 0;
				cimg_grid_workspace(i, j, 2) = 255;
			}
		}
	}

	//atualizando com dados do workspace
	cimg_forXY(cimg_grid_workspace,x,y){
		if(workspace[x][y] == 4){ //cor amarelo
			cimg_grid_workspace(x,y,0) = 255;
			cimg_grid_workspace(x,y,1) = 255;
			cimg_grid_workspace(x,y,2) = 0;
		}
		else if(workspace[x][y] == 8){ //cor preta
			cimg_grid_workspace(x,y,0) = cimg_grid_workspace(x,y,1) = cimg_grid_workspace(x,y,2) = 0;
		}
	}

	return cimg_grid_workspace;
}
//------------------------------------------------------------------------------
void destroy_matrix(int **matrix,Point dim_matrix){
	for(int i=0;i<dim_matrix.x;i++)
		delete[] matrix[i];
	delete[] matrix;
}
//------------------------------------------------------------------------------
int **build_matrix(Point dim_matrix){
	int **matrix;

	matrix = new int*[dim_matrix.x];
	for(int i=0;i < dim_matrix.x;i++)
		matrix[i] = new int[dim_matrix.y];

	for(int i=0;i<dim_matrix.x;i++)
		for(int j=0;j<dim_matrix.y;j++)
			matrix[i][j] = 0;

	return matrix;
}
//------------------------------------------------------------------------------
int **copy_matrix(int **matrix,Point dim_matrix){
	int **copy;
	copy = new int*[dim_matrix.x];
	for(int i=0;i < dim_matrix.x;i++)
		copy[i] = new int[dim_matrix.y];

	for(int i=0;i<dim_matrix.x;i++)
		for(int j=0;j<dim_matrix.y;j++)
			copy[i][j] = matrix[i][j];

	return copy;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
double **load_fileToArray(const char* fileName, Dimension *dim_data){
	int n_lines = count_lines(fileName);
	double **data;
	data = new double*[n_lines];

	dim_data->x = n_lines;

	ifstream infile;
	infile.open(fileName); // open file
	if (infile) {
		string line;
		int i = 0;
		getline(infile, line);
		string temp;
		vector<string> myVector;
		split(line, ",", &myVector);
		for(int k = 0; k < n_lines; k++)
			data[k] = new double[myVector.size()];
		dim_data->y = myVector.size();

		for (int j = 0; j < myVector.size(); j++) {
			temp = myVector.at(j);
			data[i][j] = atof(temp.c_str());
		}

		i++;
		while (!infile.eof()){
			getline(infile, line);
			vector<string> myVector;
			split(line, ",", &myVector);
			for (int j = 0; j < myVector.size(); j++) {
				temp = myVector.at(j);
				data[i][j] = atof(temp.c_str());
			}
			i++;
		}
		infile.close();
	}


	return data;
}
//-------------------------------------------------------------------------------------------------------------
int count_lines(const char* fileName) {
	int linhas = 0;
	ifstream infile;
	infile.open(fileName); // open file
	string line;
	while (getline(infile, line)) {
		linhas++;
	}
	return linhas;
}
//-------------------------------------------------------------------------------------------------------------
void printData(double **data, int x, int y) {
	printf("\n");
	for (int i = 0; i < x; i++) {
		for (int j = 0; j < y; j++)
			cout << " " <<  data[i][j];
		printf("\n");
	}
}
//-------------------------------------------------------------------------------------------------------------
void print_grid_workspace(int **grid_workspace,Point dim){
	printf("\n");
	for (int i = 0; i < dim.x; i++) {
		for (int j = 0; j < dim.y; j++)
			cout << grid_workspace[i][j];
		printf("\n");
	}

}
//-------------------------------------------------------------------------------------------------------------
void split(const std::string & str, const string & delimiters, std::vector<std::string> * elems) {

	string::size_type lastPos = str.find_first_not_of(delimiters, 0);
	string::size_type pos = str.find_first_of(delimiters, lastPos);

	while (string::npos != pos || string::npos != lastPos) {
		elems->push_back(str.substr(lastPos, pos - lastPos));
		lastPos = str.find_first_not_of(delimiters, pos);
		pos = str.find_first_of(delimiters, lastPos);
	}
}
//-------------------------------------------------------------------------------------------------------------
void transform_to_grid(Point dim_grid,Data *nDspace,int num_data,int neighborhood_size){
	srand(time(NULL));

	Point temp;

	int tam = sqrt(neighborhood_size);

	int max_x = dim_grid.x - tam;
	int min_x = tam;

	int max_y = dim_grid.y - tam;
	int min_y = tam;

	temp.x = ((float)rand()/RAND_MAX) * (max_x-min_x) + min_x;
	temp.y = ((float)rand()/RAND_MAX) * (max_y-min_y) + min_y;

	for(int i=0;i<num_data;i++){
		while(has_data(nDspace, num_data, temp)){
			temp.x = ((float)rand()/RAND_MAX) * (max_x-min_x) + min_x;
			temp.y = ((float)rand()/RAND_MAX) * (max_y-min_y) + min_y;
		}
		nDspace[i].set_grid_position(temp);
	}

}
//-------------------------------------------------------------------------------------------------------------
Data *load_data(double **data, Dimension dim_data){
	Data *nDspace;
	nDspace = new Data[dim_data.x];
	int cluster_temp = 0;

	double *features_temp;
	features_temp = new double[(dim_data.y-1)];

	for(int x=0;x<dim_data.x;x++){
		for(int y=0;y<dim_data.y-1;y++){
			features_temp[y] = data[x][y];
		}
		nDspace[x].set_number_features(dim_data.y-1);
		nDspace[x].set_features(features_temp);
		cluster_temp = data[x][dim_data.y-1];
		nDspace[x].set_cluster(cluster_temp);
		nDspace[x].set_ID(x);
		nDspace[x].set_status(true);
	}


	return nDspace;
}
//-------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------
bool has_data(Data *nDspace, int num_data, Point point){
	for(int i = 0; i < num_data; i++){
		if(nDspace[i].get_grid_position().x == point.x && nDspace[i].get_grid_position().y == point.y){
			return true;
		}
	}
	return false;
}
//-------------------------------------------------------------------------------------------------------------
bool has_ant(AntClustering *formigas, int num_formigas, Point point){
	for(int i = 0; i < num_formigas; i++){
		if(formigas[i].get_position().x == point.x && formigas[i].get_position().y == point.y){
			return true;
		}
	}
	return false;
}
//-------------------------------------------------------------------------------------------------------------
double cosine_similarity(double *v1,double *v2,int length){

        double numerator = 0.0;
        double denominator_A = 0.0;
        double denominator_B = 0.0;
        double denominator;
        int f; //feature

        for(f = 0; f < length; f++)
                numerator += v1[f]*v2[f];

        for(f = 0; f < length; f++) {
                denominator_A += pow(v1[f],2);
                denominator_B += pow(v2[f],2);
        }

        denominator = sqrt(denominator_A)*sqrt(denominator_B);

        return numerator/denominator;
}
//-------------------------------------------------------------------------------------------------------------
void set_workspace_ant_position(AntClustering *formigas,int num_formigas,Point dim_grid,int neighborhood_size){
	srand(time(NULL));
	Point temp;

	int tam = sqrt(neighborhood_size);

	int max_x = dim_grid.x - tam;
	int min_x = tam;

	int max_y = dim_grid.y - tam;
	int min_y = tam;

	temp.x = ((float) rand() / RAND_MAX) * (max_x - min_x) + min_x;
	temp.y = ((float) rand() / RAND_MAX) * (max_y - min_y) + min_y;




	for(int i=0;i<num_formigas;i++){
		while(has_ant(formigas, num_formigas, temp)){
			temp.x = ((float) rand() / RAND_MAX) * (max_x - min_x) + min_x;
			temp.y = ((float) rand() / RAND_MAX) * (max_y - min_y) + min_y;
		}
		formigas[i].set_position(temp);
	}
}
//-------------------------------------------------------------------------------------------------------------
void initialize_grid_workspace(int **grid,int **workspace,Point dim_grid,Data *nDspace,int num_data,AntClustering *formigas,int num_formigas){
	//putting data in grid
	int x= 0,y = 0;
	for(int i=0;i<num_data;i++){
		x = nDspace[i].get_grid_position().x;
		y = nDspace[i].get_grid_position().y;
		grid[x][y] = 1;
	}

	//putting ants in workspace
	for(int i=0;i<num_formigas;i++){
		x = formigas[i].get_position().x;
		y = formigas[i].get_position().y;
		workspace[x][y] = 8;
	}
}
//-------------------------------------------------------------------------------------------------------------
double get_euclidian_distance(double *v1,double *v2,int length){
	double DE = 0.000000;
	for(int i=0;i<length;i++)
		DE += pow((v1[i]-v2[i]),2.0);

    return sqrt(DE);
}
//-------------------------------------------------------------------------------------------------------------
double get_grid_euclidian_distance(Point p1,Point p2){
	double DE = 0.000000;

	DE += pow((p1.x-p2.x),2.0) + pow((p1.y-p2.y),2.0);

    return sqrt(DE);
}
//-------------------------------------------------------------------------------------------------------------
double get_max_euclidian_distance(Data *nDspace,int num_amostras){
	double max_distance = 0.0000;
	double temp = 0.00000;
	double **samples;
	int num_features = nDspace[0].get_number_features();
	samples = new double*[num_amostras];
	for(int i=0;i < num_amostras;i++)
		samples[i] = new double[num_features];


	for(int i=0;i<num_amostras;i++){
		for(int j=0;j<num_amostras;j++){
			temp = get_euclidian_distance(nDspace[i].get_features(),nDspace[j].get_features(),num_features);
			if(temp >= max_distance)
				max_distance = temp;
		}
	}


	for(int i=0;i<num_amostras;i++)
		delete[] samples[i];
	delete[] samples;

	return max_distance;
}
//-------------------------------------------------------------------------------------------------------------

#endif /* ANT_CLUSTERING_PROBLEM_H_ */
